Apple, the Apple logo, and Macintosh are registered trademarks of Apple Computer, Inc.
Mac and OpenDoc are trademarks of Apple Computer, Inc.
Using Macintosh resources from an OpenDoc part handler is a little more difficult than from an application. Part handlers are implemented as shared libraries, and the Code Fragment Manager does not automatically open the resource fork of a shared library when the library is in use. (Leaving the resource fork open all the time would cause resource conflicts among libraries and their host applications; but opening it every time a library was called would have way too much overhead.) Instead, a code fragment is responsible for remembering where its file lives, for opening the resource fork when it needs to access resources, and closing it when it’s done.
Fortunately, OpenDoc provides a utility that make this easier. It’s called UseRsrcM and it’s in the Utilities folder. You’ll need to include the header file <UseRsrcM.h> to use the routines described in this recipe.
Setting Up the Build System
You’ll need to add the utility file UseRsrcM.cpp into your build system — this might involve adding the file to your project if you use CodeWarrior, or adding it to a makefile if you use MPW.
You’ll also need to tell the build system you have a CFM initialization routine. In CodeWarrior, you enter the name of the routine in the Linker pane of the Preferences dialog. In MPW, you use the “-init name” command line flag of the ILink or PPCLink tool. The routine can be called anything you like, but the typical name is “EditorNameCFMInit”.
Initializing Your Library
If your part handler will need to access its resources (as almost any part handler will) you’ll need to provide a CFM initialization routine. This routine will be called by CFM whenever your library is instantiated; that is, whenever the first connection is made to your library by a process. As a part editor, this would be the first time an instance of your part is created. The initialization routine is the very first piece of your code to be called, well before any instances of your part class are created.
The initialization routine is passed a pointer to an initialization block. This block contains an FSSpec that gives the location on disk of the part handler library. You’ll need to pass a pointer to the initialization block to the function InitLibraryResources so it can open the library's resource fork and keep it around for when you need to access it.
You should also provide a termination routine, which CFM will call when your part editor library is unloaded. (Typically this happens when no instances of classes defined in your library are in existence, and OpenDoc decides to purge memory to free up space.) The termination routine should call CloseLibraryResources to close your library's resource fork and free up the memory occupied by its resource map (and any resources from it that haven't been purged or released.)
Bare-bones initialization and termination routines look like this:
The call to InitLibraryResources opens your library’s resource fork, but does not put it in the resource chain — this effectively makes it invisible to the Resource Manager, but allows it to be activated at a moment’s notice. The termination routine closes the resource fork and releases any memory it may have been using.
(By the way, the initialization routine is also a good place to do other one-time initializations, such as setting up globals. A common thing to do is to call Gestalt to determine whether various system services are available, and to store the results in global Boolean variables for later use. Keep in mind, though, that the initialization routine is not called every time a part is instantiated, only when the library is first linked into a process.)
Of course, simply declaring these routines is not enough. You need to tell the linker that these are special CFM routines. See the above section Setting Up The Build System, as well as your development tools' documentation, for full details.
For more information on initialization and termination routines, see Inside Macintosh: PowerPC System Software, page 1-29 and 3-27.
Accessing Your Library’s Resources
Before accessing your library’s resources (directly or indirectly), call BeginUsingLibraryResources. This routine activates your library’s resource fork and makes it the current resource file. (It also returns a magic 32-bit value that you should save for later but otherwise ignore.) You may then safely call Resource Manager routines like Get1Resource or Count1Resources, or Toolbox routines that indirectly call the Resource Manager, such as GetMenu or GetNewWindow.
As soon as possible, call EndUsingLibraryResources, passing in the magic 32-bit value you received from BeginUsingLibraryResources. This will deactivate your library’s resource fork. Leaving the resource fork active for too long can cause conflicts with other part handlers (or OpenDoc subsystems) that need to use resources. In particular, you should not make any OpenDoc API calls while your resource fork is active, or (even worse) return from a call to your part without deactivating it.
Activating and deactivating your resource fork is a very quick process, without much overhead. Don’t worry about it slowing down the system.
After calling EndUsingLibraryResources, any resources that have been loaded into memory are still there. However, since your resource file is not active and is not in the resource chain, you can’t perform any resource operations on them, such as LoadResource, GetResInfo or ReleaseResource. Before you can call any resource manager routines on a resource you’ve loaded, you need to call BeginUsingLibraryResources again.
In particular, you must activate the resource file before releasing the resource. For example:
ODSLong x = BeginUsingLibraryResources();
ReleaseResource((Handle)fMenu);
EndUsingLibraryResources(x);
You can’t call ReleaseResource when your resource fork is inactive. And you can’t just call DisposeHandle on the resource, or the Resource Manager will become confused and all hell may break loose.
For C++ Users
If you use C++, there is an alternative to using these calls, based on the standard C++ idiom of a lightweight stack-based class whose constructor sets up a state and whose destructor removes it. The class is called CUsingLibraryResources. For example, a repeat of the first example:
Declaring an instance of CUsingLibraryResources activates your resource fork. When the object goes out of scope (when the flow of control leaves the enclosing block) the resource fork is deactivated.
One nice aspect of this is that, unlike in the procedural model, you can return or break from a block containing a CUsingLibraryResources. The return or break statement will cause the object to go out of scope, and the compiler will automatically call the destructor.
A CUsingLibraryResources object is a Destructo (see the Exception Handling document) and so will automatically be destructed if it goes out of scope as a result of an exception. This means that your resource fork will automatically be deactivated if an exception is thrown out of the block: a very desirable thing to have happen. For this reason, if you use C++ it's preferable to use this form as opposed to the manual Begin...End.
Resources Are Shared!
Remember, the regular Resource Manager will only load one copy of a resource into any single process. However, any number of instances of your part may be active in a single document process. This means that, unless you explicitly use the ODReadResource utilities described in the next section, all instances of your part in a single document have to share the resources.
A common error is for a part to load a resource and then later release it, perhaps in its destructor or somUninit method. The problem is that other instances of the part might still exist in the document, and they might also have loaded the same resource. After the first part releases the resource, the other parts have invalid dangling handles and will probably end up reading garbage or corrupting the heap if they try to use the resource thereafter.
A good solution is to treat resources as globals. Note that they have the same scope (per process) as your part handler library’s global variables. This means that you can safely load a resource and assign the handle to a global variable, which can then be shared by all active instances of your part. If you release the resource, perhaps in your Purge method, set the global variable to NULL so that other instances of your part know it’s been disposed. They can then load the resource again the next time they read it. A more advanced variation on this is to keep a reference count on a resource, and release the resource when the reference count goes to zero.
Handy Resource Utilities
There are some resource-loading utilities you might want to use. These have the advantage that they don’t load the resources into the application heap (which has very little free space in an OpenDoc environment) and that the resource data isn’t shared between all instances of your part. They also take care of activating and deactivating your resource file automatically.
ODReadResource and ODReadNamedResource are comparable to GetResource and GetNamedResource, except that:
1: They explicitly load the resource out of your part handler.
2: The result is a detached handle, which means you get a new copy every time you call these routines, and also means you can dispose it normally using ODDisposeHandle.
3: They put the handle in temp memory (it was allocated via ODNewHandle.)
4: They throw exceptions if any errors occur. In particular, they throw resNotFound if the resource is not found.
ODReadResourceToPtr and ODReadNamedResourceToPtr are similar, except that they load the resource data into a nonrelocatable block and return a pointer to it. (The block is allocated via ODNewPtr and should be disposed via ODDisposePtr or MMFree.) This is obviously not appropriate for Toolbox-defined resource types like ‘PICT’ that have to be referenced via handles, but for your own types it can be preferable since the memory allocation is more efficient, and access to the data requires only single indirection.
ODGetString reads the contents of a 'STR ' resource from your editor into a Str255 that you pass in. It throws an exception (usually resNotFound) if the resource can't be read.
ODGetIndString reads a string from a 'STR#' resource from your editor into a Str255 that you pass in. It is like GetIndString except that it automatically activates and deactivates your resource fork, and throws an exception if the resource can't be found.